package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

func main() {
	s := NewSlidingWindow(10, 10)
	go func() {
		for t := range time.NewTicker(time.Second).C {
			visitNum := rand.Int63n(10)
			s.Increment(visitNum)
			fmt.Println("begin:", t, ",visitNum:", visitNum, ",times:", s.Sum(time.Now()))
		}
	}()
	select {}
}

type SlidingWindow struct {
	windowSize    int64 // 窗口大小
	timeSliceSize int64
	timeSlices    map[int64]int64
	mutex         *sync.RWMutex
}

func NewSlidingWindow(windowSize, timeSliceSize int64) *SlidingWindow {
	return &SlidingWindow{
		windowSize:    windowSize,
		timeSliceSize: timeSliceSize,
		timeSlices:    make(map[int64]int64),
		mutex:         &sync.RWMutex{},
	}
}

func (s SlidingWindow) removeOldTimeSlices() {
	now := time.Now().Unix() - s.windowSize
	for timestamp := range s.timeSlices {
		if timestamp <= now {
			delete(s.timeSlices, timestamp)
		}
	}
}

func (s SlidingWindow) Increment(i int64) {
	if i == 0 {
		return
	}
	s.mutex.Lock()
	defer s.mutex.Unlock()

	now := time.Now().Unix()
	if s.timeSlices[now]+i > s.timeSliceSize {
		s.timeSlices[now] = s.timeSliceSize
	} else {
		s.timeSlices[now] += i
	}
	fmt.Println(s.timeSlices)
	s.removeOldTimeSlices()
}

func (s SlidingWindow) Sum(now time.Time) int64 {
	s.mutex.RLock()
	defer s.mutex.RUnlock()

	sum := int64(0)
	for timestamp, value := range s.timeSlices {
		if timestamp >= now.Unix()-s.windowSize {
			sum += value
		}
	}
	return sum
}
